rollout Common_SelectFaceByUVWTool "依据UnwrapUVW选面"
(
    button btnOpenPanel "打开依据UVW选面工具" toolTip:"打开依据UVW选面工具" align:#center
	on btnOpenPanel pressed do
	(
        if Common_SelectFaceByUVW != undefined then
        (
            DestroyDialog Common_SelectFaceByUVW
        )
        createDialog Common_SelectFaceByUVW width:220
	)
)

rollout Common_VertexPaintToSelectedFaceTool "给选中面刷顶点色"
(
    button btnOpenPanel "打开给选中面刷顶点色工具" toolTip:"打开给选中面刷顶点色工具" align:#center
	on btnOpenPanel pressed do
	(
        if Common_VertexPaintToSelectedFace != undefined then
        (
            DestroyDialog Common_VertexPaintToSelectedFace
        )
        createDialog Common_VertexPaintToSelectedFace width:220
	)
)

rollout Common_NormalMappingTool "Normal映射"
(
    button btnOpenPanel "打开Normal映射工具" toolTip:"打开Normal映射工具" align:#center
	on btnOpenPanel pressed do
	(
        if Common_NoorsNormalThief != undefined then
        (
            DestroyDialog Common_NoorsNormalThief
        )
        createDialog Common_NoorsNormalThief width:220
	)
)

rollout Common_RolloutSolveExportGimbalLock "FBX GimbalLock修复"
(
	/*
	逻辑：
	填入报错骨骼
	填入相关mesh

	对报错骨骼 assume skin pose
	-- 根据报错骨骼找mesh

	for mesh
		mesh 缩放为 100
		extract skin data to mesh  新产生的mesh为 SkinData_原mesh名
		记录skin modifier上的boneList
		对skin modifier 做 collapse to
		新加skin modifier，加boneList
		传权重
		删掉 SkinData_原mesh名
	*/


	-- string1 = "-zhuozi"
	-- string2 = replace string1 1 1 "$"

	-- namesArray = for s in selection collect s.name

	fn getSkinBones obj =
	(
		max modify mode
		select obj
		
		if obj.modifiers[#Skin] != undefined do
		(
			skinBones = #()
			skinModifiers = getclassinstances Skin target:obj
			
			for s = 1 to skinModifiers.count where skinModifiers.count > 0 do
			(
				boneCount = skinOps.getnumberbones skinModifiers[s]
				for x = 1 to boneCount do
				(
					myBone = (skinOps.getBoneName skinModifiers[s] x 0)
					append skinBones (getNodeByName myBone)
				)
			)
			
			print skinBones
		)
		return skinBones
	)


	-- Not considering multi skin modifiers condition
	fn reSkins objName =
	(
		obj = getNodeByName objName
		print obj
		obj.scale = [1,1,1]
		
		-- refresh mesh size
		sliderTime = 1f
		sliderTime = 0f 
		
		skinUtils.ExtractSkinData obj
		
		max modify mode
		select obj
		
		if obj.modifiers[#Skin] != undefined do
		(
			skinBones = #()
			skinModifiers = getclassinstances Skin target:obj
			
			if skinModifiers.count > 1 then
			(	
				print skinModifiers.count
				print "Warning! Too many Skin modifiers!"
				return 0
			)
			else
			(	
				if skinModifiers.count == 1 do
				(
					boneCount = skinOps.getnumberbones skinModifiers[1]
					for i = 1 to boneCount do
					(
						myBone = (skinOps.getBoneName skinModifiers[1] i 0)
						append skinBones (getNodeByName myBone)
					)				
				)

				print skinBones

			)
			/*
			for modifierIndex = 1 to obj.modifiers.count do
			(	
				if obj.modifiers[modifierIndex] == skinModifiers[1] then 
				(
					skinModifiers[1].mirrorEnabled = off
					-- deleteModifier obj skinModifiers[1]
					maxOps.CollapseNodeTo obj modifierIndex off
				)
			)
			*/
			-- modPanel.setCurrentObject skinModifiers[1]
			modifierIndex = modPanel.getModifierIndex obj skinModifiers[1]
			maxOps.CollapseNodeTo obj modifierIndex off

			addModifier obj (Skin())
			newSkin = obj.modifiers[#Skin]
			
			skinDataObjName = "SkinData_" + objName
			skinDataObj = getNodeByName skinDataObjName
			
			for skinBoneIndex=1 to skinBones.count do
			(	
				skinBone = skinBones[skinBoneIndex]
				modPanel.setCurrentObject newSkin
				skinOps.addBone newSkin skinBone skinBoneIndex
				print skinBone
			)

			select #(obj, skinDataObj)
			skinUtils.ImportSkinDataNoDialog true false false false false 1.0 0
			-- newSkin.always_deform = false
			-- newSkin.enabled=true
			-- newSkin.always_deform = true
			delete skinDataObj
			clearSelection()
		)
	)

	fn batching boneArray meshArray=
	(
		-- boneArray = #("huatong", "shanzi", "muban", "Bone001", "Bone003", "Bone005", "zhuozi")
		-- meshArray = #("SM_af32mrr_csdesk_01", "SK_af32mrr_default_CG")

		sliderTime = 0f

		for boneName in boneArray do
		(
			myBone = getNodeByName boneName
			myBone.assumeSkinPose()
			skinModifiers = getclassinstances Skin target:myBone
			for index = 1 to skinModifiers.count do deleteModifier myBone skinModifiers[index]
		)

		for meshName in meshArray do
		(
			reSkins meshName
		)
	)

	group ""
	(
		edittext boneList "骨骼/控制器:" height:100 labelOnTop:true
		listbox meshList "模型:" height:7 items:#() readOnly:false
		
		button addMeshButton "添加模型" toolTip:"将选择的模型添加到上面的列表中" align:#center across:2 offset:[0,2]
		button deleteMeshButton "删除模型" toolTip:"删除上面的列表中选择的模型" align:#center offset:[0,2]
	)
	
	button batchingButton "开始批处理" toolTip:"1.让骨骼/控制器回到初始Pose（Assume Skin Pose）\n2.模型还原缩放到100\n3.重做Skin，还原权重"
	
	on addMeshButton pressed do
	(	
		tempArray = meshList.items
		for obj in selection do
		(	
			objName = obj.name
			if (findItem tempArray objName) != 0 or (classOf obj as string) != "PolyMeshObject" do continue
			meshList.items = append meshList.items objName
		)
	)

	on deleteMeshButton pressed do
	(
		if meshList.items.count > 0 and meshList.selection > 0 do
		(
			meshList.items = deleteItem meshList.items meshList.selection
		)
	)

	on batchingButton pressed do
	(
		boneArray = FilterString boneList.text "- \r\n"
		meshArray = meshList.items
		batching boneArray meshArray
	)
)

rollout Common_AssembleTool "动画组装工具"
(	
	button btnOpenDialogPanel "动画组装工具" toolTip:"打开动画组装工具面板" align:#center width:100
	on btnOpenDialogPanel pressed do
	(
		if assembleAnimDialog != undefined then
		(
			DestroyDialog assembleAnimDialog
		)
		createDialog assembleAnimDialog modal:false
	)
)

-- FBX可以用FBX - Merge操作合并回Max
rollout MocapBipedToolsRollout "动捕相关工具" width:250 height:244
(
    group "添加Root/*Origin点"
    (
        label lblAddRoot "请选中骨架的质心点(如:Bip01)" align:#left width:190 across:2
        button btnAddRoot "添加" align:#right width:40
    )
    group "生成TPose Fbx"
    (
        editText textFBXSuffix "Fbx文件后缀" width:160 text:"_TPose" align:#left
        checkbox chkBipedOnly "只输出CS骨骼" default:false align:#Left
        label lblTPoseExport "输出Tpose Fbx到Max文件目录" width:180 align:#left across:2
        button btnTPoseExport "输出" width:40 align:#right
    )
    group "动捕数据应用到Max文件"
    (
        label lbl1 "1  打开需要应用动捕fbx的Max文件" align:#left
        label lbl2 "2" across:3 align:#left width:10 offset:[0,8]
        edittext MocapFbxPath "" width:160 offset:[-65,6] text:"请选择动捕fbx源文件目录"
        button btnSelectMocapFbxPath "选择" width:40 offset:[0,6] height:18 align:#right
        label lbl3 "3" across:2 align:#left width:10 offset:[0,6]
        button btnatchExecute "批处理转换" offset:[-100,0] width:100 align:#left toolTip:"根据所有动捕动作fbx，依次生成对应max文件（帧速率60FPS）"
    )
	
    fn TraversalChildNode parentNode selectionArray = 
    (
        if parentNode == undefined then
        (
            return false
        )

        for childnode in parentNode.children do
        (
            if childnode.children.count > 0 then
            (
                if not (TraversalChildNode childnode selectionArray) then
                    return false
            )
            else
            (
                append selectionArray childnode
            )
        )
        append selectionArray parentNode
        return true
    )

    fn SelectSkeletonAll = 
    (
        local selectionArray = #()
        local ret = TraversalChildNode $Root selectionArray
        if ret then
        (
            selectMore selectionArray
        )
        return ret
    )

    fn GetAllBipedObjects = 
    (
        local bipedObjects = #()
        for geo in geometry where (isKindOf geo Biped_Object) do
        (
	        append bipedObjects geo
        )
        return bipedObjects
    )

	fn getFilesRecursive root pattern =
	(
		local dir_array = GetDirectories (root+"/*")
		for d in dir_array do
			join dir_array (GetDirectories (d+"/*"))
		local my_files = getFiles (root+"/*")
		for f in dir_array do
			join my_files (getFiles (f + pattern))
		return my_files
	)
	
    on btnAddRoot pressed do
    (
        if $ != undefined then
        (
            if getnodebyname "Root" == undefined then
                Dummy pos:[0,0,0] name:"Root" boxSize:[20,20,20]
            if getnodebyname "*origin" == undefined then
                Dummy pos:[0,0,0] name:"*origin" boxSize:[10,10,10]

            $.parent = $Root
            messageBox("添加Root和*Origin点成功!    ")
        )
        else
        (
            messageBox("请选中骨架的质心点!    ")
        )
    )

    on btnTPoseExport pressed do
    (
        setquietmode true
        max hold

        max unhide all #noPrompt
        local filename = maxfilepath + maxfilename
        if filename == "" then
        (
            setquietmode false
            messagebox("操作失败 Max文件异常!          ")
            return false
        )

        fbxExporterSetParam "Animation" false
        sliderTime = 0

        local rootBone = $Root
        if undefined == rootBone then
        (
            setquietmode false
            messagebox("操作失败 骨架上没有Root点!          ")
            return false
        )

        local BipedBones = GetAllBipedObjects()

        local L_Clavicle = undefined
        local L_UpperArm = undefined
        local L_Forearm = undefined
        local L_Hand = undefined

        local R_Clavicle = undefined
        local R_UpperArm = undefined
        local R_Forearm = undefined
        local R_Hand = undefined

        local L_Finger0Arr = #()
        local L_Finger1Arr = #()
        local L_Finger2Arr = #()
        local L_Finger3Arr = #()
        local L_Finger4Arr = #()

        local R_Finger0Arr = #()
        local R_Finger1Arr = #()
        local R_Finger2Arr = #()
        local R_Finger3Arr = #()
        local R_Finger4Arr = #()

        for BoneNode in BipedBones do
        (
            if (findString BoneNode.name "L Clavicle" != undefined) then
                L_Clavicle = BoneNode
            if (findString BoneNode.name "L UpperArm" != undefined) then
                L_UpperArm = BoneNode
            if (findString BoneNode.name "L Forearm" != undefined) then
                L_Forearm = BoneNode
            if (findString BoneNode.name "L Hand" != undefined) then
                L_Hand = BoneNode
            if (findString BoneNode.name "L Finger0" != undefined) then
                append L_Finger0Arr BoneNode
            if (findString BoneNode.name "L Finger1" != undefined) then
                append L_Finger1Arr BoneNode
            if (findString BoneNode.name "L Finger2" != undefined) then
                append L_Finger2Arr BoneNode
            if (findString BoneNode.name "L Finger3" != undefined) then
                append L_Finger3Arr BoneNode
            if (findString BoneNode.name "L Finger4" != undefined) then
                append L_Finger4Arr BoneNode

            if (findString BoneNode.name "R Clavicle" != undefined) then
                R_Clavicle = BoneNode
            if (findString BoneNode.name "R UpperArm" != undefined) then
                R_UpperArm = BoneNode
            if (findString BoneNode.name "R Forearm" != undefined) then
                R_Forearm = BoneNode
            if (findString BoneNode.name "R Hand" != undefined) then
                R_Hand = BoneNode
            if (findString BoneNode.name "R Finger0" != undefined) then
                append R_Finger0Arr BoneNode
            if (findString BoneNode.name "R Finger1" != undefined) then
                append R_Finger1Arr BoneNode
            if (findString BoneNode.name "R Finger2" != undefined) then
                append R_Finger2Arr BoneNode
            if (findString BoneNode.name "R Finger3" != undefined) then
                append R_Finger3Arr BoneNode
            if (findString BoneNode.name "R Finger4" != undefined) then
                append R_Finger4Arr BoneNode
        )

        if L_Clavicle != undefined then
            biped.settransform L_Clavicle #rotation ((eulerAngles 0 0 0) as quat) false
        if L_UpperArm != undefined then
            biped.settransform L_UpperArm #rotation ((eulerAngles 0 0 0) as quat) false
        if L_Forearm != undefined then
            biped.settransform L_Forearm #rotation ((eulerAngles 0 0 0) as quat) false
        if L_Hand != undefined then
            biped.settransform L_Hand #rotation ((eulerAngles -90 0 0) as quat) false
        for _bone in L_Finger0Arr do
            biped.settransform _bone #rotation ((eulerAngles -90 0 0) as quat) false
        for _bone in L_Finger1Arr do
            biped.settransform _bone #rotation ((eulerAngles -90 0 0) as quat) false
        for _bone in L_Finger2Arr do
            biped.settransform _bone #rotation ((eulerAngles -90 0 0) as quat) false
        for _bone in L_Finger3Arr do
            biped.settransform _bone #rotation ((eulerAngles -90 0 0) as quat) false
        for _bone in L_Finger4Arr do
            biped.settransform _bone #rotation ((eulerAngles -90 0 0) as quat) false

        if R_Clavicle != undefined then
            biped.settransform R_Clavicle #rotation ((eulerAngles 0 180 0) as quat) false
        if R_UpperArm != undefined then
            biped.settransform R_UpperArm #rotation ((eulerAngles 0 180 0) as quat) false
        if R_Forearm != undefined then
            biped.settransform R_Forearm #rotation ((eulerAngles 0 180 0) as quat) false
        if R_Hand != undefined then
            biped.settransform R_Hand #rotation ((eulerAngles 90 180 0) as quat) false
        for _bone in R_Finger0Arr do
            biped.settransform _bone #rotation ((eulerAngles 90 180 0) as quat) false
        for _bone in R_Finger1Arr do
            biped.settransform _bone #rotation ((eulerAngles 90 180 0) as quat) false
        for _bone in R_Finger2Arr do
            biped.settransform _bone #rotation ((eulerAngles 90 180 0) as quat) false
        for _bone in R_Finger3Arr do
            biped.settransform _bone #rotation ((eulerAngles 90 180 0) as quat) false
        for _bone in R_Finger4Arr do
            biped.settransform _bone #rotation ((eulerAngles 90 180 0) as quat) false

        clearSelection()
        if chkBipedOnly.checked then
        (
            local allBiped = GetAllBipedObjects()
            selectmore allBiped
        )
        else
        (
            SelectSkeletonAll()
        )

        filename = substring filename 1 (filename.count-4)
        filename = filename + textFBXSuffix.text + ".fbx"
        local dir = getFilenamePath filename
        if not isDirectoryWriteable dir then makeDir dir all:true

        exportFile filename #noPrompt selectedOnly:true using:FBXEXP

        max fetch
        setquietmode false

        messagebox("成功导出Tpose FBX骨架！     ")
        return true
    )

	on btnatchExecute pressed do
	(
		try
		(
			--maxfilepath 	--当前打开的文件的文件路径
			--maxfilename  	--当前打开的文件名
			RigFilePath = maxfilepath + maxfilename							--获取当前打开文件的绝对路径
			local fbxfilearray = getFilesRecursive MocapFbxPath.text "*.fbx"		--获取文件夹内所有fbx
			local filecount = fbxfilearray.count							--统计fbx数量
			pluginManager.loadClass FbxImporter
			for i = 1 to filecount do 
			(
				local exportFilePath = substituteString fbxfilearray[i] ".fbx" ".max"
				FBXImporterSetParam "Mode" #merge
				FBXImporterSetParam "Animation" true
				FBXImporterSetParam "FillTimeline" true
				FBXImporterSetParam "BakeAnimationLayers" true
				FBXImporterSetParam "PointCache" true
				loadMaxFile RigFilePath useFileUnits:true quiet:true #noPrompt    --打开rig文件
				frameRate = 30
				importFile fbxfilearray[i] #noPrompt
				frameRate = 60
				saveMaxFile exportFilePath
				resetMaxFile #noprompt		--重置maxfile
			)
			messagebox("全部转换完成！      ")
		)
		catch();
	)
	
	--获取motion data 目录
	on btnSelectMocapFbxPath pressed do
	(
		global tp_MaxfileArray=#()
		tp_Batch = getSavePath caption:"请选择动捕数据源文件目录"
        if tp_Batch == undefined then
        (
            return false
        )
		MocapFbxPath.text = tp_Batch 
		tp_MaxfileArray = getfiles (tp_Batch+"\*.fbx" as string)
		tp_Direcories = getDirectories (tp_Batch+"\*" as string)
		for i in tp_Direcories do
		(
			tp_MaxfileArrayFoo = getfiles (i+"*.fbx" as string)
			for a in tp_MaxfileArrayFoo do append tp_MaxfileArray a
		)
	)
)

rollout Common_MocapBipedTool "动捕相关工具"
(
	button btnOpenDialogPanel1 "动捕相关工具" toolTip:"打开动捕相关工具面板" align:#center width:100
	on btnOpenDialogPanel1 pressed do
	(
		if MocapBipedToolsRollout != undefined then
		(
			DestroyDialog MocapBipedToolsRollout
		)
		createDialog MocapBipedToolsRollout modal:false
	)
)

rollout Common_BatchRenameSkelName "批量重命名骨骼"
(
    group "替换规则"
    (
        label lblSourceText "将字符" align:#left offset:[0,3]
        editText edtSourceText "" align:#left width:140

        label lblReplaceText "替换为" align:#left offset:[0,3]
        editText edtReplaceText "" align:#left width:140
        
        checkbox chkAffectBiped "修改CS骨骼" checked:true
        checkbox chkAffectGeoBone "修改Bone骨骼" checked:true
        checkbox chkAffectDummyBone "修改Dummy骨骼" checked:true
        checkbox chkAffectOther "修改其它物体" checked:true
    )
    
    button btnSingleRenameSkelName "单个文件执行"

    group "批处理"
    (
        label lblSrcText "文件目录" align:#left offset:[0,3] across:2
        button lblScrSelect "选择" align:#right
        editText textSrcPath "" align:#left width:140 tooltip:"填写源文件夹路径名，例如D:\\AnimOld"

        -- button lblDstSelect "选择" align:#left across:2
        -- label lblDstText "【输出】文件目录" align:#right offset:[0,3]
        -- editText textDstPath "" align:#left width:140 tooltip:"填写导出文件夹路径名，例如D:\\AnimNew"

        button btnBatchRenameSkelName "批量文件执行"
    )

    local sourceString = ""
    local replaceString = ""

    local batchSourceFilePath = ""
    local batchDestFilePath = ""

    fn ExecuteRenameNodeNames = 
    (
        -- local bipRoots = A2UFn_GetAllBipedRootObject()
        -- for bipRoot in bipRoots do
        -- (
        --     local allChildNodes = A2UFn_GetAllChildNodes bipRoot
        --     for childnode in allChildNodes do
        --     (
        --         print(childnode.name)
        --         if (A2UFn_IsBiped childnode and chkAffectBiped.checked) or (A2UFn_IsGeoBone childnode and chkAffectGeoBone.checked) or (A2UFn_IsDummyBone childnode and chkAffectDummyBone.checked) or (chkAffectOther.checked) then
        --         (
        --             A2UFn_ReplaceNodeName childnode sourceString replaceString
        --         )
        --     )
        -- )
        
        local allNodes = #()
        allNodes = join allNodes $geometry
        allNodes = join allNodes $shapes
        allNodes = join allNodes $helpers

        for node in allNodes do
        (
            print(node.name)
            if (A2UFn_IsBiped node and chkAffectBiped.checked) or (A2UFn_IsGeoBone node and chkAffectGeoBone.checked) or (A2UFn_IsDummyBone node and chkAffectDummyBone.checked) or (chkAffectOther.checked) then
            (
                A2UFn_ReplaceNodeName node sourceString replaceString
            )
        )
    )

	on textSrcPath entered text do
	(
		batchSourceFilePath = textSrcPath.text
	)

    on textDstPath entered text do
    (
        batchDestFilePath = textSrcPath.text
    )

    on lblScrSelect pressed do
    (
        local curPath = getSavePath()
        if curPath != undefined then
        (
            batchSourceFilePath = curPath
            textSrcPath.text = curPath
        )
    )
    
	on edtSourceText entered text do
	(
        sourceString = text
    )

	on edtReplaceText entered text do
	(
        replaceString = text
    )

    on btnSingleRenameSkelName pressed do
    (
        ExecuteRenameNodeNames()
    )

    on btnBatchRenameSkelName pressed do
    (
        if queryBox "确认批量 按规则重命名 这个目录下的max文件？            \n" then
        (
            if batchSourceFilePath == "" then
            (
                messageBox("文件目录为空 \n")
                return false
            )
            
            setquietmode true
            
            for f in getfiles (batchSourceFilePath + "\\*.max") do 
            (
                loadMaxFile f usefileunits:true
                ExecuteRenameNodeNames()
                saveMaxFile f
            )

            setquietmode false
            messageBox("批量 按规则重命名 完成 \n")
        )
    )
)

rollout Common_BatchImportExportAnim "动画批量导入导出"
(
    group "批量导入导出"
    (
        button lblScrSelect "选择" align:#left across:2
        label lblSrcText "【原始】动作目录" align:#right offset:[0,3]
        editText textSrcPath "" align:#left width:140 tooltip:"填写源文件夹路径名，例如D:\\AnimOld"

        button lblDstSelect "选择" align:#left across:2
        label lblDstText "【输出】动作目录" align:#right offset:[0,3]
        editText textDstPath "" align:#left width:140 tooltip:"填写导出文件夹路径名，例如D:\\AnimNew"

        button btnRefSelect "选择" align:#left across:2
        label lblRefText "【蒙皮】文件路径" align:#right offset:[0,3]
        editText textRefName ""
    )
    checkbox chkOutputBone "导出Bone" align:#left checked:true across:2
    checkbox chkMultiBiped "多套CS" align:#right checked:false enabled:false tooltip:"多套CS骨骼，按照Root点匹配名字，分别导出.bip文件并导入"
	button btnExecute "执行"

    label seprator1 "———————————————" enabled:false offset:[0,6]
    group "读取自定义Bone数据"
    (
        checkbox chkImportCustomBoneDataOnly "启用"
        button btnSelectLoadAnim "选中模型 读取动画" enabled:false
        button btnSingleLoadAnim "单个文件 读取动画" enabled:false
        button btnSingleLoadCustomBoneData "单个文件 数据自定读取" enabled:false
        button btnBatchLoadCustomBoneData "批量 读取自定读取" enabled:false
    )
	
	local srcPath = ""
	local dstPath = ""
	local refName = ""	
    local exportBone = true
    local multiBiped = false
	
    -- getOpenFileName()
    -- getSavePath()

    on lblScrSelect pressed do
    (
        local curPath = getSavePath()
        if curPath != undefined then
        (
            srcPath = curPath
            textSrcPath.text = curPath
            A2UFn_SetMaxProgramSetting "BatchImportExportAnim" "SrcPath" srcPath
        )
    )

	on textSrcPath entered text do
	(
		srcPath = textSrcPath.text
        A2UFn_SetMaxProgramSetting "BatchImportExportAnim" "SrcPath" srcPath
	)

    on lblDstSelect pressed do
    (
        local curPath = getSavePath()
        if curPath != undefined then
        (
            dstPath = curPath
            textDstPath.text = curPath
            A2UFn_SetMaxProgramSetting "BatchImportExportAnim" "DstPath" dstPath
        )
    )

	on textDstPath entered text do
	(
		dstPath = textDstPath.text
        A2UFn_SetMaxProgramSetting "BatchImportExportAnim" "DstPath" dstPath
	)
	
    on btnRefSelect pressed do
    (
        local curFilePath = getOpenFileName()
        if curFilePath != undefined then
        (
            refName = curFilePath
            textRefName.text = refName
            A2UFn_SetMaxProgramSetting "BatchImportExportAnim" "RefName" refName
        )
    )

    on chkOutputBone changed theState do
    (
        exportBone = theState
    )

    on chkMultiBiped changed theState do
    (
        multiBiped = theState
    )

    on chkImportCustomBoneDataOnly changed theState do
    (
        btnSelectLoadAnim.enabled = theState
        btnSingleLoadAnim.enabled = theState
        btnSingleLoadCustomBoneData.enabled = theState
        btnBatchLoadCustomBoneData.enabled = theState
    )

    fn ExportAnimTxtFileAnimRange fileName = 
    (
        local rangeOutFile = createfile (fileName + "_range.txt")
        format "%\n" animationRange.start to:rangeOutFile
        format "%\n" animationRange.end to:rangeOutFile
	    close rangeOutFile
    )

    fn ExportAnimTxtFileBiped fileName = 
    (
        local bipedOutFile = createfile (fileName + "_biped.txt")
        local bipedObjs = A2UFn_GetAllBipedObject()
        
        -- bipedKeyPerFrame
        local bipedKeyPerFrameObjects = #()
		-- filter biped per frame objects (by checking the node property "ik space" is set to "object")
        for obj in bipedObjs do
        (
            for i = 1 to obj.controller.keys.count do
            (
                if (biped.getKey obj.controller i).type == #body and (biped.getKey obj.controller i).ikSpace == 1 then
                (
                    append bipedKeyPerFrameObjects obj
                    exit
                )
            )
        )

        for i = animationRange.start to animationRange.end do
        (
            format "#%\n" i to:bipedOutFile
            at time i
            (
                for obj in bipedKeyPerFrameObjects do
                (
                    local _nodeName = obj.name
                    local _position = Biped.getTransform obj #pos
                    local _rotation = Biped.getTransform obj #rotation
                    local _scale = Biped.getTransform obj #scale
                    local str = "@" + _nodeName + "@" + (_position as string) + "@" + (_rotation as string) + "@" + (_scale as string) + "\n"
                    format "%" str to:bipedOutFile
                )
            )
        )
        close bipedOutFile
    )

    fn ExportAnimTxtFileBonePRS fileName = 
    (
        local boneOutFile = createfile (fileName + "_bone.txt")
        local boneObjs = #()
		local boneObjects = A2UFn_GetAllBoneObject()
		local helperObjects = A2UFn_GetAllHelper()
		local shapeObjects = A2UFn_GetAllShape()
		join boneObjs boneObjects
		join boneObjs helperObjects
		join boneObjs shapeObjects

        for obj in boneObjs do
        (
            DebugPrint("Current is object: " + obj.name)
            -- Max.chm Link_Constraint - superclass: Matrix3Controller
            local curController = obj.controller
            if (classOf (curController) == prs) then
            (
                local str = "#" + obj.name + "\n"

                -- 在上面塌陷+屏蔽动画层之后不应该再有AnimLayer
                local haveAnimLayer = false
                if classOf(obj.position.controller) == Position_Layer or classOf(obj.rotation.controller) == Rotation_Layer or classOf(obj.scale.controller) == Scale_Layer then
                (
                    haveAnimLayer = true
                    print("### Node Have AnimLayer：" + obj.name)
                )
                
                if haveAnimLayer then
                (
                    for _frametime = animationRange.start to animationRange.end do
                    (
                        at time _frametime
                        (
                            local selfMat = obj.transform
                            local _position = selfMat.translationpart
                            local _rotation = selfMat.rotationpart
                            local _scale = selfMat.scalepart
                            str = str + "@" + "prs" + "@" + (_frametime as string) + "@" + (_position as string) + "@" + (_rotation as string) + "@" + (_scale as string) + "\n"
                        )
                    )
                )
                else
                (
                    local positionKeys = obj.position.controller.keys
                    local rotationKeys = obj.rotation.controller.keys
                    local scaleKeys = obj.scale.controller.keys

                    DebugPrint("Position Keys Num:" + positionKeys.count as string)
                    DebugPrint("Rotation Keys Num:" + rotationKeys.count as string)
                    DebugPrint("Scale Keys Num:" + scaleKeys.count as string)

                    local keys = #()

                    if positionKeys.count > 0 then
                    (
                        for k in positionKeys do
                        (
                            if k.time >= animationRange.start and k.time <= animationRange.end then
                            (
                                appendIfUnique keys k.time
                            )
                        )
                    )
                    if rotationKeys.count > 0 then
                    (
                        for k in rotationKeys do
                        (
                            if k.time >= animationRange.start and k.time <= animationRange.end then
                            (
                                appendIfUnique keys k.time
                            )
                        )
                    )
                    if scaleKeys.count > 0 then
                    (
                        for k in scaleKeys do
                        (
                            if k.time >= animationRange.start and k.time <= animationRange.end then
                            (
                                appendIfUnique keys k.time
                            )
                        )
                    )

                    if keys.count > 0 then
                    (
                        for key in keys do
                        (
                            local _frametime = key
                            at time _frametime
                            (
                                local selfMat = obj.transform
                                local _position = selfMat.translationpart
                                local _rotation = selfMat.rotationpart
                                local _scale = selfMat.scalepart
                                str = str + "@" + "prs" + "@" + (_frametime as string) + "@" + (_position as string) + "@" + (_rotation as string) + "@" + (_scale as string) + "\n"
                            )
                        )
                    )
                    else
                    (
                        local _frametime = animationRange.start
                        at time _frametime
                        (
                            local selfMat = obj.transform
                            local _position = selfMat.translationpart
                            local _rotation = selfMat.rotationpart
                            local _scale = selfMat.scalepart
                            str = str + "@" + "prs" + "@" + (_frametime as string) + "@" + (_position as string) + "@" + (_rotation as string) + "@" + (_scale as string) + "\n"
                        )
                    )
                )
                format "%" str to:boneOutFile
            )
        )
    )

    fn ExportAnimTxtFileBoneLinked fileName = 
    (
        local boneLinkedOutFile = createfile (fileName + "_bone_linked.txt")
        local boneObjs = #()
        local bipedObjs = A2UFn_GetAllBipedObject()
		local boneObjects = A2UFn_GetAllBoneObject()
		local helperObjects = A2UFn_GetAllHelper()
		local shapeObjects = A2UFn_GetAllShape()
		join boneObjs boneObjects
		join boneObjs helperObjects
		join boneObjs shapeObjects

        for obj in boneObjs do
        (
            DebugPrint("Current is object: " + obj.name)
            -- Max.chm Link_Constraint - superclass: Matrix3Controller
            local curController = obj.controller
            if (classOf (curController) == Link_Constraint) then
            (
                local str = "#" + obj.name + "\n"
                format "%" str to:boneLinkedOutFile
                
                local numLinkTargets = curController.getNumTargets()
                for i = 1 to numLinkTargets do
                (
                    local _linkto = curController.getNode i
                    local _linkname = "#LinkToWorld#"
                    if _linkto != undefined then
                    (
                        _linkname = _linkto.name
                    )
                    local _keymode = curController.key_mode
                    local _linkframe = curController.getFrameNo i
                    local _position = (curController.link_params.position)
                    local _rotation = (curController.link_params.rotation)
                    local _scale = (curController.link_params.scale)

                    local str = "@" + "Link_Constraint" + "@" + (_keymode as string) + "@" + (_linkname as string) + "@" + (_linkframe as string) + "@" + (_position as string) + "@" + (_rotation as string) + "@" + (_scale as string) + "\n"
                    format "%" str to:boneLinkedOutFile
                )

                local linkParams = curController.link_params
                local positionKeys = linkParams.position.controller.keys
                local rotationKeys = linkParams.rotation.controller.keys
                local scaleKeys = linkParams.scale.controller.keys

                DebugPrint("Linked Position Keys Num:" + positionKeys.count as string)
                DebugPrint("Linked Rotation Keys Num:" + rotationKeys.count as string)
                DebugPrint("Linked Scale Keys Num:" + scaleKeys.count as string)

                -- Link_Constraint_Scale
                if scaleKeys.count > 0 then
                (
                    for k in scaleKeys do
                    (
                        if k.time >= animationRange.start and k.time <= animationRange.end then
                        (
                            at time k.time
                            (
                                local str = "@" + "Link_Constraint_Scale" + "@" + (k.time as string) + "@" + (linkParams.scale as string) + "\n"
                                format "%" str to:boneLinkedOutFile
                            )
                        )
                    )
                )
                else
                (
                    at time animationRange.start
                    (
                        local str = "@" + "Link_Constraint_Scale" + "@" + (animationRange.start as string) + "@" + (linkParams.scale as string) + "\n"
                        format "%" str to:boneLinkedOutFile
                    )
                )

                -- Link_Constraint_Rotation
                if rotationKeys.count > 0 then
                (
                    for k in rotationKeys do
                    (
                        if k.time >= animationRange.start and k.time <= animationRange.end then
                        (
                            at time k.time
                            (
                                local str = "@" + "Link_Constraint_Rotation" + "@" + (k.time as string) + "@" + (linkParams.rotation as string) + "\n"
                                format "%" str to:boneLinkedOutFile
                            )
                        )
                    )
                )
                else
                (
                    at time animationRange.start
                    (
                        local str = "@" + "Link_Constraint_Rotation" + "@" + (animationRange.start as string) + "@" + (linkParams.rotation as string) + "\n"
                        format "%" str to:boneLinkedOutFile
                    )
                )

                -- Link_Constraint_Translation
                if positionKeys.count > 0 then
                (
                    for k in positionKeys do
                    (
                        if k.time >= animationRange.start and k.time <= animationRange.end then
                        (
                            at time k.time
                            (
                                local str = "@" + "Link_Constraint_Translation" + "@" + (k.time as string) + "@" + (linkParams.position as string) + "\n"
                                format "%" str to:boneLinkedOutFile
                            )
                        )
                    )
                )
                else
                (
                    at time animationRange.start
                    (
                        local str = "@" + "Link_Constraint_Translation" + "@" + (animationRange.start as string) + "@" + (linkParams.position as string) + "\n"
                        format "%" str to:boneLinkedOutFile
                    )
                )
            )
        )
    )

    fn ExportAnimation = 
    (
        /*
        local dir_array = GetDirectories (srcPath+"*")
        for d in dir_array do
            ExportAnimation d
        */

        local ExportFailedMaxFileNames = ""

        for f in getfiles (srcPath + "\\*.max") do 
        (
            print ("[Export Animation] " + f)
            loadMaxFile f usefileunits:true

            local fileName = getFilenameFile maxFileName
            local animFilePath = dstPath + "\\AnimFiles\\" + fileName
            
            ExportAnimTxtFileAnimRange animFilePath

            -- Unhide All NoneBone Geometry
            hideByCategory.none()
            hideByCategory.geometry = true

            setquietmode true
            max unhide all #noPrompt
            
            for i = 1 to GetNumberDisplayFilters() do
            (
                SetDisplayFilter i false
            )

            max select all

            local allBipObjects = A2UFn_GetAllBipedObject()
            local bipRoots = #()
            
            -- [Vertical_Horizontal_Turn] : Matrix3Controller
            -- This class represents the transform controller for the root object within the biped system. 
            for obj in allBipObjects do
            (
                if classof(obj.controller) == Vertical_Horizontal_Turn then
                    append bipRoots obj
            )
            
            if multiBiped == false and bipRoots.count > 1 then
            (
                messagebox ("导出失败！有多套CS骨骼，且未勾选【多套CS】选项")
                print ("导出失败！有多套CS骨骼，且未勾选【多套CS】选项 " + maxFileName)
				continue
            )
            
            if bipRoots.count > 0 then
            (
                if multiBiped == false then
                (
                    local bipFilePath = dstPath + "\\AnimFiles\\" + fileName + ".bip"
                    local controller = bipRoots[1].controller
                    biped.saveBipFileSegment controller bipFilePath animationRange.start animationRange.end
                )
                else
                (
                    for obj in bipRoots do
                    (
                        local bipFilePath = dstPath + "\\AnimFiles\\" + fileName + obj.name + ".bip"
                        local controller = obj.controller
                        biped.saveBipFileSegment controller bipFilePath animationRange.start animationRange.end
                    )
                )
            )

            if exportBone and bipRoots.count > 0 then
            (
                local xafFilePath = dstPath + "\\AnimFiles\\" + fileName + ".xaf"
                
                clearSelection()
                local boneObjects = A2UFn_GetAllBoneObject()
                local helperObjects = A2UFn_GetAllHelper()
                local shapeObjects = A2UFn_GetAllShape()        -- 这里不做检查 无论是否设置为 use as bone
                selectmore boneObjects
                selectmore helperObjects
                selectmore shapeObjects
                local success = LoadSaveAnimation.saveAnimation xafFilePath $ #() #()
                if success == false then
                (
                    print("Save Animation Failed For File " + maxFileName)
                    ExportFailedMaxFileNames = ExportFailedMaxFileNames + (maxFileName + "\n")
                )
            )
            
            clearSelection()
        )

        if ExportFailedMaxFileNames != "" then
        (
            messageBox("以下Bone动画导出失败:           \n" + ExportFailedMaxFileNames)
        )
    )

    fn ImportAnimInternal fileName = 
    (
        print("******************** ImportAnimInternal " + fileName)

        hideByCategory.none()
        hideByCategory.geometry = true
        
        setquietmode true
        max unhide all #noPrompt

        for i = 1 to GetNumberDisplayFilters() do
        (
            SetDisplayFilter i false
        )
        
        local allBipObjects = A2UFn_GetAllBipedObject()
        local bipRoots = #()
        if multiBiped == false and bipRoots.count > 1 then
        (
            messagebox ("导出失败！有多套CS骨骼，请勾选【多套CS】选项")
            return false
        )

        for o in allBipObjects do
        (
            if classof(o.controller) == Vertical_Horizontal_Turn then
                append bipRoots o
        )

        -- CS骨骼
        if bipRoots.count > 0 then
        (
            if multiBiped == false then
            (
                local obj = bipRoots[1]
                local bipFilePath = dstPath + "\\AnimFiles\\" + fileName + ".bip"
                local controller = obj.controller
                biped.loadBipFile controller bipFilePath
            )
            else
            (
                for obj in bipRoots do
                (
                    local bipFilePath = dstPath + "\\AnimFiles\\" + fileName + obj.name + ".bip"
                    local controller = obj.controller
                    biped.loadBipFile controller bipFilePath
                )
            )
        )
        
        -- Bone骨骼
        if exportBone then
        (
            local xafFilePath = dstPath + "\\AnimFiles\\" + fileName + ".xaf"
            
            -- 防止有些bone选不中
            local boneObjects = A2UFn_GetAllBoneObject()
            local helperObjects = A2UFn_GetAllHelper()
            local shapeObjects = A2UFn_GetAllShape()
            clearSelection()
            selectmore boneObjects
            selectmore helperObjects
            selectmore shapeObjects
            local success = LoadSaveAnimation.loadAnimation xafFilePath $ relative:false insert:false
            if success == false then
            (
                print("Load Animation Failed For File " + maxFileName)
            )
        )
    )

    fn ImportAnimTxtFileInternal fileName = 
    (
        local rangeOutFileName = (fileName + "_range.txt")
        local bipedPerFrameOutFileName = (fileName + "_biped.txt")
        local boneOutFileName = (fileName + "_bone.txt")
        local boneLinkedOutFileName = (fileName + "_bone_linked.txt")

        local rangeOutTextArray = #()
        local bipedOutFileArray = #()
        local boneOutFileArray = #()
        local boneLinkedOutFileArray = #()

        local rangeOutFile = (openFile rangeOutFileName mode:"r")
        if rangeOutFile != undefined then
        (
            while not eof rangeOutFile do
            (
                append rangeOutTextArray (readline rangeOutFile)
            )
            close rangeOutFile
                
            animationRange = interval (execute rangeOutTextArray[1]) (execute rangeOutTextArray[2])
            slidertime = animationRange.start
        )

        local bipedOutFile = (openFile bipedPerFrameOutFileName mode:"r")
        if bipedOutFile != undefined then
        (
            while not eof bipedOutFile do
            (
                append bipedOutFileArray (readline bipedOutFile)
            )
            close bipedOutFile
        )

        local boneOutFile = (openFile boneOutFileName mode:"r")
        if boneOutFile != undefined then
        (
            while not eof boneOutFile do
            (
                append boneOutFileArray (readline boneOutFile)
            )
            close boneOutFile
        )

        local boneLinkedOutFile = (openFile boneLinkedOutFileName mode:"r")
        if boneLinkedOutFile != undefined then
        (
            while not eof boneLinkedOutFile do
            (
                append boneLinkedOutFileArray (readline boneLinkedOutFile)
            )
            close boneLinkedOutFile
        )

        with animate on
        (
            local curNode = undefined
            local curNodeKeyTimes = #()

            local curFrameTime = undefined
            
            for txt in bipedOutFileArray do
            (
                if txt[1] == "#" then
                (
                    local _frametime = execute (substring txt 2 txt.count)
                    curFrameTime = _frametime
                )
                else if txt[1] == "@" then
                (
                    local transformDataInfo = filterString txt "@"
                    if transformDataInfo.count == 4 then
                    (
                        local _nodeName = transformDataInfo[1]
                        local _position = execute transformDataInfo[2]
                        local _rotation = execute transformDataInfo[3]
                        local _scale = execute transformDataInfo[4]
                        curNode = getNodeByName _nodeName
                        if curNode != undefined then
                        (
                            at time curFrameTime
                            (
                                Biped.setTransform curNode #pos _position true
                                Biped.setTransform curNode #rotation _rotation true
                                Biped.setTransform curNode #scale _scale true
                            )
                        )
                    )
                )
            )

			if exportBone then
            (
				for txt in boneLinkedOutFileArray do
				(
					-- 新的Node
					if txt[1] == "#" then
					(
						local nodeName = substring txt 2 txt.count
						curNode = getNodeByName nodeName
                        if (isKindOf curNode Biped_Object) then
                        (
                            curNode = undefined
                        )
					)

					-- 更新Node每个关键帧时间
					else if txt[1] == "@" then
					(
						local Info = filterString txt "@"
                        if (Info[1] == "Link_Constraint") then
                        (
                            local _keyMode = execute Info[2]
                            local _linktoName = Info[3]
                            local _linkframe = execute Info[4]
                            local _position = execute Info[5]
                            local _rotation = execute Info[6]
                            local _scale = execute Info[7]

                            print("### Link_Constraint" + curNode.name + "   keymode" + (_keyMode as string))

                            if not (classOf (curNode.controller) == Link_Constraint) then
                            (
                                curNode.Controller = Link_Constraint()
                            )
                            curNode.controller.key_mode = _keyMode

                            if _linktoName == "#LinkToWorld#" then
                            (
                                curNode.controller.addWorld frameNo:_linkframe
                            )
                            else
                            (
                                local _linktoObj = getNodeByName _linktoName
                                if _linktoObj != undefined then
                                (
                                    curNode.controller.addTarget _linktoObj _linkframe
                                )
                            )
                            
                            at time _linkframe
                            (
                                curNode.controller.Link_Params.Scale = _scale
                                curNode.controller.Link_Params.Rotation = _rotation
                                curNode.controller.Link_Params.Position = _position
                            )
                        )
                        else
                        (
                            if Info[1] == "Link_Constraint_Scale" then
                            (
                                local _frametime = execute Info[2]
                                local _scale = execute Info[3]
                                at time _frametime
                                (
                                    curNode.transform.controller.link_params.scale = _scale
                                    addNewKey curNode.transform.controller.link_params.scale.controller _frametime
                                )
                            )
                            else if Info[1] == "Link_Constraint_Rotation" then
                            (
                                local _frametime = execute Info[2]
                                local _rotation = execute Info[3]
                                at time _frametime
                                (
                                    curNode.transform.controller.link_params.rotation = (inverse _rotation)
                                    addNewKey curNode.transform.controller.link_params.rotation.controller _frametime
                                )
                            )
                            else if Info[1] == "Link_Constraint_Translation" then
                            (
                                local _frametime = execute Info[2]
                                local _position = execute Info[3]
                                at time _frametime
                                (
                                    curNode.transform.controller.link_params.position = _position
                                    addNewKey curNode.transform.controller.link_params.position.controller _frametime
                                )
                            )
                        )
					)
				)
                
                for txt in boneOutFileArray do
                (
                    -- 新的Node
                    if txt[1] == "#" then
                    (
                        local nodeName = substring txt 2 txt.count
                        curNode = getNodeByName nodeName
                        if (isKindOf curNode Biped_Object) then
                        (
                            curNode = undefined
                        )
                    )
                    -- 更新Node每个关键帧时间
                    else if txt[1] == "@" then
                    (
                        if curNode != undefined then
                        (
                            local Info = filterString txt "@"

                            if (Info[1] == "prs" and Info.count == 5) then
                            (
                                if not (classOf (curNode.controller) == prs) then
                                (
                                    curNode.Controller = prs()
                                )

                                local _frametime = execute Info[2]
                                local _position = execute Info[3]
                                local _rotation = execute Info[4]
                                local _scale = execute Info[5]

                                at time _frametime
                                (
                                    (in coordsys world curNode.scale = _scale)
                                    (in coordsys world curNode.rotation = inverse _rotation)
                                    (in coordsys world curNode.position = _position)
                                )
                            )
                        )
                    )
                )
			)
        )
    )

    fn ImportAnimation = 
    (
        for f in getfiles (dstPath + "\\*.max") where f != refName do 
        (
            print ("[ImportAnimation] " + f)
            loadMaxFile f usefileunits:true
            
            local fileName = getFilenameFile maxFileName
            local animFilePath = dstPath + "\\AnimFiles\\" + fileName

            --删除所有关键帧 TODO
            A2UFn_ClearAllKeyFrames()
            
            ImportAnimInternal animFilePath
            saveMaxFile f
        )
    )

    fn ImportCustomData = 
    (
        for f in getfiles (dstPath + "\\*.max") where f != refName do 
        (
            print ("[ImportCustomData] " + f)
            loadMaxFile f usefileunits:true

            local fileName = getFilenameFile maxFileName
    
            local animFilePath = dstPath + "\\AnimFiles\\" + fileName
            ImportAnimTxtFileInternal animFilePath
            
            saveMaxFile f
        )
    )
    
    fn ImportAnimationAndCustomData = 
    (
        for f in getfiles (dstPath + "\\*.max") where f != refName do 
        (
            print ("[ImportAnimationAndCustomData] " + f)
            loadMaxFile f usefileunits:true
            
            local fileName = getFilenameFile maxFileName
            local animFilePath = dstPath + "\\AnimFiles\\" + fileName

            --删除所有关键帧 TODO
            A2UFn_ClearAllKeyFrames()
            
            ImportAnimInternal animFilePath
            ImportAnimTxtFileInternal animFilePath
            
            saveMaxFile f
        )
    )
    
    fn ReimportAnimForNonLinkedBones = 
    (
        for f in getfiles (dstPath + "\\*.max") where f != refName do 
        (
            loadMaxFile f usefileunits:true
            
            local fileName = getFilenameFile maxFileName
            print("[ReimportAnimForNonLinkedBones]" + fileName)

            -- Bone骨骼
            if exportBone then
            (
                local xafFilePath = dstPath + "\\AnimFiles\\" + fileName + ".xaf"
                
                clearSelection()

                -- 防止有些bone选不中
                local allBones = #()
                local boneObjects = A2UFn_GetAllBoneObject()
                local helperObjects = A2UFn_GetAllHelper()
                local shapeObjects = A2UFn_GetAllShape()

                for k in boneObjects do
                    appendIfUnique allBones k

                for k in helperObjects do
                    appendIfUnique allBones k

                for k in shapeObjects do
                    appendIfUnique allBones k
                
                clearSelection()
                for curBone in allBones do
                (
                    local curController = curBone.controller
                    if (classOf (curController) == prs) then
                    (
                        selectmore curBone
                    )
                )

                local success = LoadSaveAnimation.loadAnimation xafFilePath $ relative:false insert:false
                if success == false then
                (
                    print("Reload Animation Failed For File " + maxFileName)
                )
                clearSelection()
            )
        )
    )

    --Reload Max File to Prevent Unknown Exception
    fn ExportCustomDataBiped = 
    (
        for f in getfiles (srcPath + "\\*.max") do 
        (
            print ("[ExportCustomDataBiped] " + f)
            loadMaxFile f usefileunits:true
            local fileName = getFilenameFile maxFileName
            local animFilePath = dstPath + "\\AnimFiles\\" + fileName
            ExportAnimTxtFileBiped animFilePath
        )
    )

    --Reload Max File to Prevent Unknown Exception
    fn ExportCustomDataBonePRS = 
    (
        for f in getfiles (srcPath + "\\*.max") do 
        (
            print ("[ExportCustomDataBonePRS] " + f)
            loadMaxFile f usefileunits:true
            local fileName = getFilenameFile maxFileName
            local animFilePath = dstPath + "\\AnimFiles\\" + fileName
            ExportAnimTxtFileBonePRS animFilePath
        )
    )

    --Reload Max File to Prevent Unknown Exception
    fn ExportCustomDataBoneLinked = 
    (
        for f in getfiles (srcPath + "\\*.max") do 
        (
            print ("[ExportCustomDataBoneLinked] " + f)
            loadMaxFile f usefileunits:true
            local fileName = getFilenameFile maxFileName
            local animFilePath = dstPath + "\\AnimFiles\\" + fileName
            ExportAnimTxtFileBoneLinked animFilePath
        )
    )

    -- Copy Maxfile from srcPath to dstPath
    fn CopyMaxFile = 
    (
        for f in getfiles (srcPath + "\\*.max") do 
        (
            local targetFileName = getFilenameFile f
            local generatedMaxFilePath = dstPath + "\\" + targetFileName + ".max"
            copyFile refName generatedMaxFilePath
            print("[CopyMaxFile] Copy File To " + generatedMaxFilePath)
        )
    )

    fn ConfigCheck = 
    (
        if srcPath == "" then
        (
			messagebox("原始路径未设置        \n")
            return false
        )
		else if dstPath == "" then
        (
			messagebox("目标路径未设置        \n")
            return false
        )
		else if refName == "" then
        (
			messagebox("蒙皮文件未设置        \n")
            return false
        )
        else if not (isDirectoryWriteable dstPath) then
        (
            messagebox("导出路径不存在        \n")
            return false
        )
		else if not (doesFileExist refName) then
        (
            messagebox("蒙皮文件不存在        \n")
            return false
        )
        return true
    )

    fn MakeExportFolder = 
    (
        TempPath = dstPath + "\\AnimFiles"
        makeDir TempPath
    )

    fn BatchExportImportAnims = 
    (
        MakeExportFolder()

        ExportAnimation()
        
        resetMaxFile #noPrompt
        ExportCustomDataBiped()

        if exportBone then
        (
            resetMaxFile #noPrompt
            ExportCustomDataBonePRS()
            resetMaxFile #noPrompt
            ExportCustomDataBoneLinked()
        )

        resetMaxFile #noPrompt
        CopyMaxFile()

        resetMaxFile #noPrompt
        ImportAnimation()

        resetMaxFile #noPrompt
        ImportCustomData()

        -- ImportAnimationAndCustomData()
        ReimportAnimForNonLinkedBones()
    )

    on btnExecute pressed do
	(
		if ConfigCheck() == true then
        (
            setquietmode true
			BatchExportImportAnims()
            setquietmode false
		)
	)

    on btnSelectLoadAnim pressed do
    (
        print(maxFileName)
        local fileName = getFilenameFile maxFileName
        local xafFilePath = dstPath + "\\AnimFiles\\" + fileName + ".xaf"
        
        -- 防止有些bone选不中
        local success = LoadSaveAnimation.loadAnimation xafFilePath $ relative:false insert:false
        if success == false then
        (
            print("Load Animation Failed For File " + maxFileName)
        )
    )

    on btnSingleLoadAnim pressed do
    (
        local fileName = getFilenameFile maxFileName
        local xafFilePath = dstPath + "\\AnimFiles\\" + fileName + ".xaf"
        
        -- 防止有些bone选不中
        local boneObjects = A2UFn_GetAllBoneObject()
        local helperObjects = A2UFn_GetAllHelper()
        local shapeObjects = A2UFn_GetAllShape()
        clearSelection()
        selectmore boneObjects
        selectmore helperObjects
        selectmore shapeObjects
        local success = LoadSaveAnimation.loadAnimation xafFilePath $ relative:false insert:false
        if success == false then
        (
            print("Load Animation Failed For File " + maxFileName)
        )
    )

    on btnSingleLoadCustomBoneData pressed do
    (
        print("******************** Import Custom Anim ")
        local fileName = getFilenameFile maxFileName
        local animFilePath = dstPath + "\\AnimFiles\\" + fileName
        ImportAnimTxtFileInternal animFilePath
    )

    on btnBatchLoadCustomBoneData pressed do
    (
        ImportCustomData()
    )
    
    on Common_BatchImportExportAnim open do 
    (
        -- init BatchImportExportAnim Params
        srcPath = A2UFn_GetMaxProgramSetting "BatchImportExportAnim" "SrcPath"
        textSrcPath.text = srcPath

        dstPath = A2UFn_GetMaxProgramSetting "BatchImportExportAnim" "DstPath"
        textDstPath.text = dstPath

        refName = A2UFn_GetMaxProgramSetting "BatchImportExportAnim" "refName"
        textRefName.text = refName
    )
)

rollout Common_DebugTool "Debug工具"
(
    group "Select"
    (
        button btnSelectMoreBiped "加选CS骨骼"
        button btnSelectMoreBone "加选Bone骨骼"
        button btnSelectMoreMesh "加选模型"
    )

    group "AnimKeys"
    (
        button btnClearSelectKeys "清除 选中物体Keys"
        button btnClearAllKeys "清除 所有物体Keys"
    )

    group "AnimRange"
    (
        spinner spinnerAnimationRangeStart "Start" type:#integer range:[-1000000,1000000,animationRange.start] align:#left across:2
		spinner spinnerAnimationRangeEnd "End" type:#integer range:[-1000000,1000000,animationRange.end] align:#right
        button btnSetAnimRange "设置动画范围"
    )

    group "Print"
    (
        button btnPrintAllSelectedTransform "选中骨骼Transform"
        button btnPrintAllBoneTransform "所有骨骼Transform"
        button btnPrintSelectedTransformKeys "选中骨骼Transform的Keys"
    )

    group "Debug"
    (
        button btnTest1 "Debug 1" across:2
        button btnTest2 "Debug 2"
        button btnTest3 "Debug 3" across:2
        button btnTest4 "Debug 4"
    )

	on btnSelectMoreBiped pressed do
	(
		local a = A2UFn_GetAllBipedObject()
		selectmore a
	)
	
	on btnSelectMoreBone pressed do
	(
		local a = A2UFn_GetAllBoneObject()
		local b = A2UFn_GetAllShapeAsBone()
		local c = A2UFn_GetAllHelper()
		selectmore a
		selectmore b
		selectmore c
	)

	on btnSelectMoreMesh pressed do
	(
		local a = A2UFn_GetAllMesh()
		selectmore a
	)

    on btnClearSelectKeys pressed do
    (
        A2UFn_ClearKeyFrames $
    )

    on btnClearAllKeys pressed do
    (
        if queryBox "确认清除所有的Node的关键帧？            \n" then
        (
            A2UFn_ClearAllKeyFrames()
        )
    )

    on btnSetAnimRange pressed do
    (
        A2UFn_SetAnimRange spinnerAnimationRangeStart.value spinnerAnimationRangeEnd.value
    )

	on btnPrintAllSelectedTransform pressed do
	(
		local selectedNodes = getCurrentSelection()
		local _start = animationrange.start
		local _end = animationrange.end
		for t = _start to _end do
		(
			A2UFn_PrintNodesTransformAtFrame selectedNodes t
		)
	)

	on btnPrintAllBoneTransform pressed do
	(
		local _start = animationrange.start
		local _end = animationrange.end
	)

    on btnPrintSelectedTransformKeys pressed do
    (
        local selectedNodes = getCurrentSelection()
        local obj = selectedNodes[1]

        if obj == undefined then
        (
            messageBox("当前没有选中任何Node")
            return false
        )
        else if selectedNodes.count > 1 then
        (
            messageBox("请不要选择超过1个Node")
            return false
        )

		local _start = animationrange.start
		local _end = animationrange.end
        
        local keys = #()
        local str = ""

        for k in obj.position.controller.keys do
        (
            appendIfUnique keys k.time
        )
        for k in obj.rotation.controller.keys do
        (
            appendIfUnique keys k.time
        )
        for k in obj.scale.controller.keys do
        (
            appendIfUnique keys k.time
        )

        print "***************** Position Keys  *****************"
        print obj.position.controller.keys
        print "***************** Rotation Keys  *****************"
        print obj.rotation.controller.keys
        print "***************** Scale Keys  *****************"
        print obj.scale.controller.keys
        print "***************** All Transform Keys  *****************"

        sort keys

        if keys.count > 0 then
        (
            for key in keys do
            (
                local _frametime = key
                at time _frametime
                (
                    local selfMat = obj.transform
                    local _position = selfMat.translationpart
                    local _rotation = selfMat.rotationpart
                    local _scale = selfMat.scalepart
                    str = str + "Frame:" + (_frametime as string) + " Pos:" + (_position as string) + " Rotation:" + (_rotation as string) + " Scale:" + (_scale as string) + "\n"
                )
            )
        )
        else
        (
            local _frametime = animationRange.start
            at time _frametime
            (
                local selfMat = obj.transform
                local _position = selfMat.translationpart
                local _rotation = selfMat.rotationpart
                local _scale = selfMat.scalepart
                str = str + "Frame:" + (_frametime as string) + " Pos:" + (_position as string) + " Rotation:" + (_rotation as string) + " Scale:" + (_scale as string) + "\n"
            )
        )

        print str
    )

	on btnTest1 pressed do
	(
	)

	on btnTest2 pressed do
	(
	)

	on btnTest3 pressed do
	(
	)

	on btnTest4 pressed do
	(
	)

)